home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / edit / elv18src.zip / cmd2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-02  |  18.0 KB  |  991 lines

  1. /* cmd2.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains some of the commands - mostly ones that change text */
  12.  
  13. #ifdef AIX
  14. # define _XOPEN_SOURCE
  15. # include <sys/mode.h>
  16. # include <sys/stat.h>
  17. # undef _XOPEN_SOURCE
  18. #endif
  19. #include "config.h"
  20. #include "ctype.h"
  21. #include "vi.h"
  22. #include "regexp.h"
  23. #if TOS
  24. # include <stat.h>
  25. #else
  26. # if OSK
  27. #  include "osk.h"
  28. # else
  29. #  if AMIGA
  30. #   include "amistat.h"
  31. #  else
  32. #   include <sys/stat.h>
  33. #  endif
  34. # endif
  35. #endif
  36.  
  37.  
  38. /*ARGSUSED*/
  39. void cmd_substitute(frommark, tomark, cmd, bang, extra)
  40.     MARK    frommark;
  41.     MARK    tomark;
  42.     CMD    cmd;
  43.     int    bang;
  44.     char    *extra;    /* rest of the command line */
  45. {
  46.     char    *line;    /* a line from the file */
  47.     regexp    *re;    /* the compiled search expression */
  48.     char    *subst;    /* the substitution string */
  49.     char    *opt;    /* substitution options */
  50.     long    l;    /* a line number */
  51.     char    *s, *d;    /* used during subtitutions */
  52.     char    *conf;    /* used during confirmation */
  53.     long    chline;    /* # of lines changed */
  54.     long    chsub;    /* # of substitutions made */
  55.     static    optp;    /* boolean option: print when done? */
  56.     static    optg;    /* boolean option: substitute globally in line? */
  57.     static    optc;    /* boolean option: confirm before subst? */
  58. #ifndef CRUNCH
  59.     long    oldnlines;
  60. #endif
  61.  
  62.  
  63.     /* for now, assume this will fail */
  64.     rptlines = -1L;
  65.  
  66.     if (cmd == CMD_SUBAGAIN)
  67.     {
  68. #ifndef NO_MAGIC
  69.         if (*o_magic)
  70.             subst = "~";
  71.         else
  72. #endif
  73.         subst = "\\~";
  74.         re = regcomp("");
  75.  
  76.         /* if visual "&", then turn off the "p" and "c" options */
  77.         if (bang)
  78.         {
  79.             optp = optc = FALSE;
  80.         }
  81.     }
  82.     else /* CMD_SUBSTITUTE */
  83.     {
  84.         /* make sure we got a search pattern */
  85.         if (!*extra)
  86.         {
  87.             msg("Usage: s/regular expression/new text/");
  88.             return;
  89.         }
  90.  
  91.         /* parse & compile the search pattern */
  92.         subst = parseptrn(extra);
  93.         re = regcomp(extra + 1);
  94.     }
  95.  
  96.     /* abort if RE error -- error message already given by regcomp() */
  97.     if (!re)
  98.     {
  99.         return;
  100.     }
  101.  
  102.     if (cmd == CMD_SUBSTITUTE)
  103.     {
  104.         /* parse the substitution string & find the option string */
  105.         for (opt = subst; *opt && *opt != *extra; opt++)
  106.         {
  107.             if (*opt == '\\' && opt[1])
  108.             {
  109.                 opt++;
  110.             }
  111.         }
  112.         if (*opt)
  113.         {
  114.             *opt++ = '\0';
  115.         }
  116.  
  117.         /* analyse the option string */
  118.         if (!*o_edcompatible)
  119.         {
  120.             optp = optg = optc = FALSE;
  121.         }
  122.         while (*opt)
  123.         {
  124.             switch (*opt++)
  125.             {
  126.               case 'p':    optp = !optp;    break;
  127.               case 'g':    optg = !optg;    break;
  128.               case 'c':    optc = !optc;    break;
  129.               case ' ':
  130.               case '\t':            break;
  131.               default:
  132.                 msg("Subst options are p, c, and g -- not %c", opt[-1]);
  133.                 return;
  134.             }
  135.         }
  136.     }
  137.  
  138.     /* if "c" or "p" flag was given, and we're in visual mode, then NEWLINE */
  139.     if ((optc || optp) && mode == MODE_VI)
  140.     {
  141.         addch('\n');
  142.         exrefresh();
  143.     }
  144.  
  145.     ChangeText
  146.     {
  147.         /* reset the change counters */
  148.         chline = chsub = 0L;
  149.  
  150.         /* for each selected line */
  151.         for (l = markline(frommark); l <= markline(tomark); l++)
  152.         {
  153.             /* fetch the line */
  154.             line = fetchline(l);
  155.  
  156.             /* if it contains the search pattern... */
  157.             if (regexec(re, line, TRUE))
  158.             {
  159.                 /* increment the line change counter */
  160.                 chline++;
  161.  
  162.                 /* initialize the pointers */
  163.                 s = line;
  164.                 d = tmpblk.c;
  165.  
  166.                 /* do once or globally ... */
  167.                 do
  168.                 {
  169. #ifndef CRUNCH
  170.                     /* confirm, if necessary */
  171.                     if (optc)
  172.                     {
  173.                         for (conf = line; conf < re->startp[0]; conf++)
  174.                             addch(*conf);
  175.                         standout();
  176.                         for ( ; conf < re->endp[0]; conf++)
  177.                             addch(*conf);
  178.                         standend();
  179.                         for (; *conf; conf++)
  180.                             addch(*conf);
  181.                         addch('\n');
  182.                         exrefresh();
  183.                         if (getkey(0) != 'y')
  184.                         {
  185.                             /* copy accross the original chars */
  186.                             while (s < re->endp[0])
  187.                                 *d++ = *s++;
  188.  
  189.                             /* skip to next match on this line, if any */
  190.                             goto Continue;
  191.                         }
  192.                     }
  193. #endif /* not CRUNCH */
  194.  
  195.                     /* increment the substitution change counter */
  196.                     chsub++;
  197.  
  198.                     /* copy stuff from before the match */
  199.                     while (s < re->startp[0])
  200.                     {
  201.                         *d++ = *s++;
  202.                     }
  203.  
  204.                     /* substitute for the matched part */
  205.                     regsub(re, subst, d);
  206.                     s = re->endp[0];
  207.                     d += strlen(d);
  208.  
  209. Continue:
  210.                     /* if this regexp could conceivably match
  211.                      * a zero-length string, then require at
  212.                      * least 1 unmatched character between
  213.                      * matches.
  214.                      */
  215.                     if (re->minlen == 0)
  216.                     {
  217.                         if (!*s)
  218.                             break;
  219.                         *d++ = *s++;
  220.                     }
  221.  
  222.                 } while (optg && regexec(re, s, FALSE));
  223.  
  224.                 /* copy stuff from after the match */
  225.                 while (*d++ = *s++)    /* yes, ASSIGNMENT! */
  226.                 {
  227.                 }
  228.  
  229. #ifndef CRUNCH
  230.                 /* NOTE: since the substitution text is allowed to have ^Ms which are
  231.                  * translated into newlines, it is possible that the number of lines
  232.                  * in the file will increase after each line has been substituted.
  233.                  * we need to adjust for this.
  234.                  */
  235.                 oldnlines = nlines;
  236. #endif
  237.  
  238.                 /* replace the old version of the line with the new */
  239.                 d[-1] = '\n';
  240.                 d[0] = '\0';
  241.                 change(MARK_AT_LINE(l), MARK_AT_LINE(l + 1), tmpblk.c);
  242.  
  243. #ifndef CRUNCH
  244.                 l += nlines - oldnlines;
  245.                 tomark += MARK_AT_LINE(nlines - oldnlines);
  246. #endif
  247.  
  248.                 /* if supposed to print it, do so */
  249.                 if (optp)
  250.                 {
  251.                     addstr(tmpblk.c);
  252.                     exrefresh();
  253.                 }
  254.  
  255.                 /* move the cursor to that line */
  256.                 cursor = MARK_AT_LINE(l);
  257.             }
  258.         }
  259.     }
  260.  
  261.     /* free the regexp */
  262.     _free_(re);
  263.  
  264.     /* if done from within a ":g" command, then finish silently */
  265.     if (doingglobal)
  266.     {
  267.         rptlines = chline;
  268.         rptlabel = "changed";
  269.         return;
  270.     }
  271.  
  272.     /* Reporting */
  273.     if (chsub == 0)
  274.     {
  275.         msg("Substitution failed");
  276.     }
  277.     else if (chline >= *o_report)
  278.     {
  279.         msg("%ld substitutions on %ld lines", chsub, chline);
  280.     }
  281.     rptlines = 0L;
  282. }
  283.  
  284.  
  285.  
  286.  
  287. /*ARGSUSED*/
  288. void cmd_delete(frommark, tomark, cmd, bang, extra)
  289.     MARK    frommark;
  290.     MARK    tomark;
  291.     CMD    cmd;
  292.     int    bang;
  293.     char    *extra;
  294. {
  295.     MARK    curs2;    /* an altered form of the cursor */
  296.  
  297.     /* choose your cut buffer */
  298.     if (*extra == '"')
  299.     {
  300.         extra++;
  301.     }
  302.     if (*extra)
  303.     {
  304.         cutname(*extra);
  305.     }
  306.  
  307.     /* make sure we're talking about whole lines here */
  308.     frommark = frommark & ~(BLKSIZE - 1);
  309.     tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
  310.  
  311.     /* yank the lines */
  312.     cut(frommark, tomark);
  313.  
  314.     /* if CMD_DELETE then delete the lines */
  315.     if (cmd != CMD_YANK)
  316.     {
  317.         curs2 = cursor;
  318.         ChangeText
  319.         {
  320.             /* delete the lines */
  321.             delete(frommark, tomark);
  322.         }
  323.         if (curs2 > tomark)
  324.         {
  325.             cursor = curs2 - tomark + frommark;
  326.         }
  327.         else if (curs2 > frommark)
  328.         {
  329.             cursor = frommark;
  330.         }
  331.     }
  332. }
  333.  
  334.  
  335. /*ARGSUSED*/
  336. void cmd_append(frommark, tomark, cmd, bang, extra)
  337.     MARK    frommark;
  338.     MARK    tomark;
  339.     CMD    cmd;
  340.     int    bang;
  341.     char    *extra;
  342. {
  343.     long    l;    /* line counter */
  344.  
  345. #ifndef CRUNCH
  346.     /* if '!' then toggle auto-indent */
  347.     if (bang)
  348.     {
  349.         *o_autoindent = !*o_autoindent;
  350.     }
  351. #endif
  352.  
  353.     ChangeText
  354.     {
  355.         /* if we're doing a change, delete the old version */
  356.         if (cmd == CMD_CHANGE)
  357.         {
  358.             /* delete 'em */
  359.             cmd_delete(frommark, tomark, cmd, bang, extra);
  360.         }
  361.  
  362.         /* new lines start at the frommark line, or after it */
  363.         l = markline(frommark);
  364.         if (cmd == CMD_APPEND)
  365.         {
  366.              l++;
  367.         }
  368.  
  369.         /* get lines until no more lines, or "." line, and insert them */
  370.         while (vgets('\0', tmpblk.c, BLKSIZE) >= 0)
  371.         {
  372.             addch('\n');
  373.             if (!strcmp(tmpblk.c, "."))
  374.             {
  375.                 break;
  376.             }
  377.  
  378.             strcat(tmpblk.c, "\n");
  379.             add(MARK_AT_LINE(l), tmpblk.c);
  380.             l++;
  381.         }
  382.     }
  383.  
  384.     /* on the odd chance that we're calling this from vi mode ... */
  385.     redraw(MARK_UNSET, FALSE);
  386. }
  387.  
  388.  
  389. /*ARGSUSED*/
  390. void cmd_put(frommark, tomark, cmd, bang, extra)
  391.     MARK    frommark;
  392.     MARK    tomark;
  393.     CMD    cmd;
  394.     int    bang;
  395.     char    *extra;
  396. {
  397.     /* choose your cut buffer */
  398.     if (*extra == '"')
  399.     {
  400.         extra++;
  401.     }
  402.     if (*extra)
  403.     {
  404.         cutname(*extra);
  405.     }
  406.  
  407.     /* paste it */
  408.     ChangeText
  409.     {
  410.         cursor = paste(frommark, TRUE, FALSE);
  411.     }
  412. }
  413.  
  414.  
  415. /*ARGSUSED*/
  416. void cmd_join(frommark, tomark, cmd, bang, extra)
  417.     MARK    frommark;
  418.     MARK    tomark;
  419.     CMD    cmd;
  420.     int    bang;
  421.     char    *extra;
  422. {
  423.     long    l;
  424.     char    *scan;
  425.     int    len;    /* length of the new line */
  426.  
  427.     /* if only one line is specified, assume the following one joins too */
  428.     if (markline(frommark) == nlines)
  429.     {
  430.         msg("Nothing to join with this line");
  431.         return;
  432.     }
  433.     if (markline(frommark) == markline(tomark))
  434.     {
  435.         tomark += BLKSIZE;
  436.     }
  437.  
  438.     /* get the first line */
  439.     l = markline(frommark);
  440.     strcpy(tmpblk.c, fetchline(l));
  441.     len = strlen(tmpblk.c);
  442.  
  443.     /* build the longer line */
  444.     while (++l <= markline(tomark))
  445.     {
  446.         /* get the next line */
  447.         scan = fetchline(l);
  448.  
  449.         /* remove any leading whitespace */
  450.         while (*scan == '\t' || *scan == ' ')
  451.         {
  452.             scan++;
  453.         }
  454.  
  455.         /* see if the line will fit */
  456.         if (strlen(scan) + len + 3 > (unsigned)BLKSIZE)
  457.         {
  458.             msg("Can't join -- the resulting line would be too long");
  459.             return;
  460.         }
  461.  
  462.         /* catenate it, with a space (or two) in between */
  463.         if (!bang)
  464.         {
  465.             if (len >= 1)
  466.             {
  467.                 if (tmpblk.c[len - 1] == '.'
  468.                  || tmpblk.c[len - 1] == '?'
  469.                  || tmpblk.c[len - 1] == '!')
  470.                 {
  471.                      tmpblk.c[len++] = ' ';
  472.                      tmpblk.c[len++] = ' ';
  473.                 }
  474.                 else if (tmpblk.c[len - 1] != ' ')
  475.                 {
  476.                      tmpblk.c[len++] = ' ';
  477.                 }
  478.             }
  479.         }
  480.         strcpy(tmpblk.c + len, scan);
  481.         len += strlen(scan);
  482.     }
  483.     tmpblk.c[len++] = '\n';
  484.     tmpblk.c[len] = '\0';
  485.  
  486.     /* make the change */
  487.     ChangeText
  488.     {
  489.         frommark &= ~(BLKSIZE - 1);
  490.         tomark &= ~(BLKSIZE - 1);
  491.         tomark += BLKSIZE;
  492.         change(frommark, tomark, tmpblk.c);
  493.     }
  494.  
  495.     /* Reporting... */
  496.     rptlines = markline(tomark) - markline(frommark) - 1L;
  497.     rptlabel = "joined";
  498. }
  499.  
  500.  
  501.  
  502. /*ARGSUSED*/
  503. void cmd_shift(frommark, tomark, cmd, bang, extra)
  504.     MARK    frommark;
  505.     MARK    tomark;
  506.     CMD    cmd;
  507.     int    bang;
  508.     char    *extra;
  509. {
  510.     long    l;    /* line number counter */
  511.     int    oldidx;    /* number of chars previously used for indent */
  512.     int    newidx;    /* number of chars in the new indent string */
  513.     int    oldcol;    /* previous indent amount */
  514.     int    newcol;    /* new indent amount */
  515.     int    shift;    /* number of columns to shift by */
  516.     char    *text;    /* pointer to the old line's text */
  517.  
  518. #ifdef CRUNCH
  519.     shift = (*o_shiftwidth & 0xff);
  520. #else /* not CRUNCH */
  521.     /* figure out the shift amount */
  522.     for (shift = 1; *extra == '<' || *extra == '>'; extra++, shift++)
  523.     {
  524.     }
  525.     shift *= (*o_shiftwidth & 0xff);
  526. #endif /* not CRUNCH */
  527.  
  528.     ChangeText
  529.     {
  530.         /* for each line to shift... */
  531.         for (l = markline(frommark); l <= markline(tomark); l++)
  532.         {
  533.             /* get the line - ignore empty lines unless ! mode */
  534.             text = fetchline(l);
  535.             if (!*text && !bang)
  536.                 continue;
  537.  
  538.             /* calc oldidx and oldcol */
  539.             for (oldidx = 0, oldcol = 0;
  540.                  text[oldidx] == ' ' || text[oldidx] == '\t';
  541.                  oldidx++)
  542.             {
  543.                 if (text[oldidx] == ' ')
  544.                 {
  545.                     oldcol += 1;
  546.                 }
  547.                 else
  548.                 {
  549.                     oldcol += *o_tabstop - (oldcol % *o_tabstop);
  550.                 }
  551.             }
  552.  
  553.             /* calc newcol */
  554.             if (cmd == CMD_SHIFTR)
  555.             {
  556.                 newcol = oldcol + shift;
  557.             }
  558.             else
  559.             {
  560.                 newcol = oldcol - shift;
  561.                 if (newcol < 0)
  562.                     newcol = 0;
  563.             }
  564.  
  565.             /* if no change, then skip to next line */
  566.             if (oldcol == newcol)
  567.                 continue;
  568.  
  569.             /* build a new indent string */
  570.             newidx = 0;
  571.             if (*o_autotab)
  572.             {
  573.                 while (newcol >= *o_tabstop)
  574.                 {
  575.                     tmpblk.c[newidx++] = '\t';
  576.                     newcol -= *o_tabstop;
  577.                 }
  578.             }
  579.             while (newcol > 0)
  580.             {
  581.                 tmpblk.c[newidx++] = ' ';
  582.                 newcol--;
  583.             }
  584.             tmpblk.c[newidx] = '\0';
  585.  
  586.             /* change the old indent string into the new */
  587.             change(MARK_AT_LINE(l), MARK_AT_LINE(l) + oldidx, tmpblk.c);
  588.         }
  589.     }
  590.  
  591.     /* Reporting... */
  592.     rptlines = markline(tomark) - markline(frommark) + 1L;
  593.     if (cmd == CMD_SHIFTR)
  594.     {
  595.         rptlabel = ">ed";
  596.     }
  597.     else
  598.     {
  599.         rptlabel = "<ed";
  600.     }
  601. }
  602.  
  603.  
  604. /*ARGSUSED*/
  605. void cmd_read(frommark, tomark, cmd, bang, extra)
  606.     MARK    frommark;
  607.     MARK    tomark;
  608.     CMD    cmd;
  609.     int    bang;
  610.     char    *extra;
  611. {
  612.     int    fd, rc;    /* used while reading from the file */
  613.     char    *scan;    /* used for finding NUL characters */
  614.     int    hadnul;    /* boolean: any NULs found? */
  615.     int    addnl;    /* boolean: forced to add newlines? */
  616.     int    len;    /* number of chars in current line */
  617.     long    lines;    /* number of lines in current block */
  618.     struct stat statb;
  619.  
  620.     /* special case: if ":r !cmd" then let the filter() function do it */
  621.     if (extra[0] == '!')
  622.     {
  623.         filter(frommark, MARK_UNSET, extra + 1, TRUE);
  624.         return;
  625.     }
  626.  
  627.     /* open the file */
  628.     fd = open(extra, O_RDONLY);
  629.     if (fd < 0)
  630.     {
  631.         msg("Can't open \"%s\"", extra);
  632.         return;
  633.     }
  634.  
  635. #ifndef CRUNCH
  636.     if (stat(extra, &statb) < 0)
  637.     {
  638.         msg("Can't stat \"%s\"", extra);
  639.     }
  640. # if TOS
  641.     if (statb.st_mode & S_IJDIR)
  642. # else
  643. #  if OSK
  644.     if (statb.st_mode & S_IFDIR)
  645. #  else
  646.     if ((statb.st_mode & S_IFMT) != S_IFREG)
  647. #  endif
  648. # endif
  649.     {
  650.         msg("\"%s\" is not a regular file", extra);
  651.         return;
  652.     }
  653. #endif /* not CRUNCH */
  654.  
  655.     /* get blocks from the file, and add them */
  656.     ChangeText
  657.     {
  658.         /* insertion starts at the line following frommark */
  659.         tomark = frommark = (frommark | (BLKSIZE - 1L)) + 1L;
  660.         len = 0;
  661.         hadnul = addnl = FALSE;
  662.  
  663.         /* add an extra newline, so partial lines at the end of
  664.          * the file don't trip us up
  665.          */
  666.         add(tomark, "\n");
  667.  
  668.         /* for each chunk of text... */
  669.         while ((rc = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
  670.         {
  671.             /* count newlines, convert NULs, etc. ... */
  672.             for (lines = 0, scan = tmpblk.c; rc > 0; rc--, scan++)
  673.             {
  674.                 /* break up long lines */
  675.                 if (*scan != '\n' && len + 2 > BLKSIZE)
  676.                 {
  677.                     *scan = '\n';
  678.                     addnl = TRUE;
  679.                 }
  680.  
  681.                 /* protect against NUL chars in file */
  682.                 if (!*scan)
  683.                 {
  684.                     *scan = 0x80;
  685.                     hadnul = TRUE;
  686.                 }
  687.  
  688.                 /* starting a new line? */
  689.                 if (*scan == '\n')
  690.                 {
  691.                     /* reset length at newline */
  692.                     len = 0;
  693.                     lines++;
  694.                 }
  695.                 else
  696.                 {
  697.                     len++;
  698.                 }
  699.             }
  700.  
  701.             /* add the text */
  702.             *scan = '\0';
  703.             add(tomark, tmpblk.c);
  704.             tomark += MARK_AT_LINE(lines) + len - markidx(tomark);
  705.         }
  706.  
  707.         /* if partial last line, then retain that first newline */
  708.         if (len > 0)
  709.         {
  710.             msg("Last line had no newline");
  711.             tomark += BLKSIZE; /* <- for the rptlines calc */
  712.         }
  713.         else /* delete that first newline */
  714.         {
  715.             delete(tomark, (tomark | (BLKSIZE - 1L)) + 1L);
  716.         }
  717.     }
  718.  
  719.     /* close the file */
  720.     close(fd);
  721.  
  722.     /* Reporting... */
  723.     rptlines = markline(tomark) - markline(frommark);
  724.     rptlabel = "read";
  725.     if (mode == MODE_EX)
  726.     {
  727.         cursor = (tomark & ~BLKSIZE) - BLKSIZE;
  728.     }
  729.     else
  730.     {
  731.         cursor = frommark;
  732.     }
  733.  
  734.     if (addnl)
  735.         msg("Newlines were added to break up long lines");
  736.     if (hadnul)
  737.         msg("NULs were converted to 0x80");
  738. }
  739.  
  740.  
  741.  
  742. /*ARGSUSED*/
  743. void cmd_undo(frommark, tomark, cmd, bang, extra)
  744.     MARK    frommark;
  745.     MARK    tomark;
  746.     CMD    cmd;
  747.     int    bang;
  748.     char    *extra;
  749. {
  750.     undo();
  751. }
  752.  
  753.  
  754. /* print the selected lines */
  755. /*ARGSUSED*/
  756. void cmd_print(frommark, tomark, cmd, bang, extra)
  757.     MARK    frommark;
  758.     MARK    tomark;
  759.     CMD    cmd;
  760.     int    bang;
  761.     char    *extra;
  762. {
  763.     REG char    *scan;
  764.     REG long    l;
  765.     REG int        col;
  766.  
  767.     for (l = markline(frommark); l <= markline(tomark); l++)
  768.     {
  769.         /* display a line number, if CMD_NUMBER */
  770.         if (cmd == CMD_NUMBER)
  771.         {
  772.             sprintf(tmpblk.c, "%6ld  ", l);
  773.             qaddstr(tmpblk.c);
  774.             col = 8;
  775.         }
  776.         else
  777.         {
  778.             col = 0;
  779.         }
  780.  
  781.         /* get the next line & display it */
  782.         for (scan = fetchline(l); *scan; scan++)
  783.         {
  784.             /* expand tabs to the proper width */
  785.             if (*scan == '\t' && cmd != CMD_LIST)
  786.             {
  787.                 do
  788.                 {
  789.                     qaddch(' ');
  790.                     col++;
  791.                 } while (col % *o_tabstop != 0);
  792.             }
  793.             else if (*scan >= 1 && *scan < ' ' || *scan == '\177')
  794.             {
  795.                 qaddch('^');
  796.                 qaddch(*scan ^ 0x40);
  797.                 col += 2;
  798.             }
  799.             else if ((*scan & 0x80) && cmd == CMD_LIST)
  800.             {
  801.                 sprintf(tmpblk.c, "\\%03o", UCHAR(*scan));
  802.                 qaddstr(tmpblk.c);
  803.                 col += 4;
  804.             }
  805.             else
  806.             {
  807.                 qaddch(*scan);
  808.                 col++;
  809.             }
  810.  
  811.             /* wrap at the edge of the screen */
  812.             if (!has_AM && col >= COLS)
  813.             {
  814.                 addch('\n');
  815.                 col -= COLS;
  816.             }
  817.         }
  818.         if (cmd == CMD_LIST)
  819.         {
  820.             qaddch('$');
  821.         }
  822.         addch('\n');
  823.         exrefresh();
  824.     }
  825.  
  826.     /* leave the cursor on the last line printed */
  827.     cursor = tomark;
  828. }
  829.  
  830.  
  831. /* move or copy selected lines */
  832. /*ARGSUSED*/
  833. void cmd_move(frommark, tomark, cmd, bang, extra)
  834.     MARK    frommark;
  835.     MARK    tomark;
  836.     CMD    cmd;
  837.     int    bang;
  838.     char    *extra;
  839. {
  840.     MARK    destmark;
  841.  
  842.     /* parse the destination linespec.  No defaults.  Line 0 is okay */
  843.     destmark = cursor;
  844.     if (!strcmp(extra, "0"))
  845.     {
  846.         destmark = 0L;
  847.     }
  848.     else if (linespec(extra, &destmark) == extra || !destmark)
  849.     {
  850.         msg("invalid destination address");
  851.         return;
  852.     }
  853.  
  854.     /* flesh the marks out to encompass whole lines */
  855.     frommark &= ~(BLKSIZE - 1);
  856.     tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
  857.     destmark = (destmark & ~(BLKSIZE - 1)) + BLKSIZE;
  858.  
  859.     /* make sure the destination is valid */
  860.     if (cmd == CMD_MOVE && destmark >= frommark && destmark < tomark)
  861.     {
  862.         msg("invalid destination address");
  863.     }
  864.  
  865.     /* Do it */
  866.     ChangeText
  867.     {
  868.         /* save the text to a cut buffer */
  869.         cutname('\0');
  870.         cut(frommark, tomark);
  871.  
  872.         /* if we're not copying, delete the old text & adjust destmark */
  873.         if (cmd != CMD_COPY)
  874.         {
  875.             delete(frommark, tomark);
  876.             if (destmark >= frommark)
  877.             {
  878.                 destmark -= (tomark - frommark);
  879.             }
  880.         }
  881.  
  882.         /* add the new text */
  883.         paste(destmark, FALSE, FALSE);
  884.     }
  885.  
  886.     /* move the cursor to the last line of the moved text */
  887.     cursor = destmark + (tomark - frommark) - BLKSIZE;
  888.     if (cursor < MARK_FIRST || cursor >= MARK_LAST + BLKSIZE)
  889.     {
  890.         cursor = MARK_LAST;
  891.     }
  892.  
  893.     /* Reporting... */
  894.     rptlabel = ( (cmd == CMD_COPY) ? "copied" : "moved" );
  895. }
  896.  
  897.  
  898.  
  899. /* execute EX commands from a file */
  900. /*ARGSUSED*/
  901. void cmd_source(frommark, tomark, cmd, bang, extra)
  902.     MARK    frommark;
  903.     MARK    tomark;
  904.     CMD    cmd;
  905.     int    bang;
  906.     char    *extra;
  907. {
  908.     /* must have a filename */
  909.     if (!*extra)
  910.     {
  911.         msg("\"source\" requires a filename");
  912.         return;
  913.     }
  914.  
  915.     doexrc(extra);
  916. }
  917.  
  918.  
  919. #ifndef NO_AT
  920. /*ARGSUSED*/
  921. void cmd_at(frommark, tomark, cmd, bang, extra)
  922.     MARK    frommark;
  923.     MARK    tomark;
  924.     CMD    cmd;
  925.     int    bang;
  926.     char    *extra;
  927. {
  928.     static    nest = FALSE;
  929.     int    result;
  930.     char    buf[MAXRCLEN];
  931.     char    *scan, *end;
  932.  
  933.     /* don't allow nested macros */
  934.     if (nest)
  935.     {
  936.         msg("@ macros can't be nested");
  937.         return;
  938.     }
  939.     nest = TRUE;
  940.  
  941.     /* require a buffer name */
  942.     if (*extra == '"')
  943.         extra++;
  944.     if (!*extra || !isascii(*extra) ||!islower(*extra))
  945.     {
  946.         msg("@ requires a cut buffer name (a-z)");
  947.     }
  948.  
  949.     /* get the contents of the buffer */
  950.     result = cb2str(*extra, buf, (unsigned)(sizeof buf));
  951.     if (result <= 0)
  952.     {
  953.         msg("buffer \"%c is empty", *extra);
  954.     }
  955.     else if (result >= sizeof buf)
  956.     {
  957.         msg("buffer \"%c is too large to execute", *extra);
  958.     }
  959.     else
  960.     {
  961.         /* execute the contents of the buffer as ex commands */
  962.         for (scan = buf; scan; scan = end)
  963.         {
  964.  
  965.             /* locate the end of the command line */
  966.             for (end = buf; *end && *end != '\n'; end++)
  967.             {
  968.             }
  969.  
  970.             /* convert the '\n' to '\0' at the end */
  971.             if (*end)
  972.             {
  973.                 *end++ = '\0';
  974.             }
  975.             else
  976.             {
  977.                 end = (char *)0;
  978.             }
  979.  
  980.             /* Execute the command line, unless it is blank. */
  981.             if (*buf)
  982.             {
  983.                 doexcmd(buf, '\\');
  984.             }
  985.         }
  986.     }
  987.  
  988.     nest = FALSE;
  989. }
  990. #endif
  991.